#include "CertificateEntry.hpp"

using namespace BFT::messages;

CertificateEntry::CertificateEntry( boost::shared_ptr<HistoryDigest> _hd,
				    boost::shared_ptr<CommandBatch> _commands,
				    boost::shared_ptr<NonDeterminism> _nd,
				    boost::shared_ptr<Digest> _cp )
{
	hd = _hd;
	commands = _commands;
	nd = _nd;
	cphash = _cp;
	bytes_size = 0;
}

CertificateEntry::CertificateEntry( boost::shared_ptr<char> _bytes, int _bytes_size, int _offset)
{
	int offset = _offset;
	
	//history digest
	int temp_hd_size = HistoryDigest::size();
	boost::shared_ptr<char> temp_hd ( (char*)malloc( temp_hd_size ), free );
	memcpy( (void*)(temp_hd.get()), (const void *)(_bytes.get()+offset), temp_hd_size);
	hd = HistoryDigest::fromBytes(temp_hd,temp_hd_size);
	offset += temp_hd_size;

	//cphash
	int temp_cphash_size = Digest::size();
	boost::shared_ptr<char> temp_cph ( (char *)malloc( temp_cphash_size ), free );
	memcpy( (void*)(temp_cph.get()), (const void *)(_bytes.get()+offset), temp_cphash_size);
	cphash = Digest::fromBytes(temp_cph, temp_cphash_size);
	offset += temp_cphash_size;
	
	//nondeterminism size 
	//TODO treated as a variable length field, though the size of a nondet is fixed...
	uint16_t temp_nd_size;
	temp_nd_size = ntohs( *(uint16_t*)(_bytes.get()+offset) );
	offset += NON_DETERMINISM_SIZE_SIZE;
	
	//nondeterminism
	boost::shared_ptr<char> temp_nd ( (char*)malloc( temp_nd_size ), free );
	memcpy( (void*)(temp_nd.get()), (const void *)(_bytes.get()+offset), temp_nd_size);
	nd.reset(new NonDeterminism(temp_nd,temp_nd_size));
	offset += temp_nd_size;
	
	//commandbatchsize
	uint16_t temp_commands_size;
	temp_commands_size = ntohs( *(uint16_t*)(_bytes.get()+offset) );
	offset += CE_COMMAND_BATCH_SIZE_SIZE;
	
	//number of entries in the command batch
	uint16_t temp_entries_count;
	temp_entries_count = ntohs( *(uint16_t*)(_bytes.get()+offset) );
	offset += ENTRY_COUNT_SIZE;	
	
	//commandbatch itself (finally)
	boost::shared_ptr<char> temp_commands ( (char*)malloc( temp_commands_size ), free );
	memcpy( (void*)(temp_commands.get()), (const void *)(_bytes.get()+offset), temp_commands_size);
	commands.reset (new CommandBatch(temp_commands,temp_commands_size,temp_entries_count));
	offset += temp_commands_size;
	
	//ok, fields filled.
	
	bytes_size = 0;
	
	//TODO fill also bytes, so that everithing is ready. check for possible performance degradations
//	bytes.reset ( (char*)malloc( offset - _offset ), free );
//	memcpy( (void*) bytes.get() , ( const void *) (_bytes.get() + _offset ), offset- _offset);
}

boost::shared_ptr<char> CertificateEntry::getBytes(int& _bytes_size)
{
	if (bytes_size == 0)
	{
		int offset=0;
		//history_digest
		int temp_hd_size = hd.get()->getSize();
		boost::shared_ptr<char> temp_hd;
		temp_hd = hd.get()->getBytes();

		//cphash
		int temp_cph_size = cphash.get()->getSize();
		boost::shared_ptr<char> temp_cph;
		temp_cph = cphash.get()->getBytes();
		
		//nondeterminism
		int temp_nd_size = nd.get()->getSize();
		boost::shared_ptr<char> temp_nd;
		temp_nd = nd.get()->getBytes();
		
		//command_batch
		int temp_commands_size;
		boost::shared_ptr<char> temp_commands = commands.get()->getBytes(temp_commands_size);
		int temp_entry_count = commands.get()->getEntryCount();
		
		bytes_size = temp_hd_size +  //size of history digest
			     temp_cph_size + // size of cphash
			     NON_DETERMINISM_SIZE_SIZE + //size of the size of nondeterminism
			     temp_nd_size + //size of the nondeterminism
			     CE_COMMAND_BATCH_SIZE_SIZE + //size of the command batch
			     ENTRY_COUNT_SIZE + //size fo the entry count
			     temp_commands_size; //size of the command batch
	
		bytes.reset( (char*)malloc(sizeof(char) * bytes_size) , free );
		
		//write down the history_digest
		memcpy( (void*)bytes.get(), (const void*)temp_hd.get(), temp_hd_size );
		offset += temp_hd_size;

		memcpy( (void*)bytes.get(), (const void*)temp_cph.get(), temp_cph_size );
		offset += temp_cph_size;
		
		//write down the nondeterminism size
		*(uint16_t*)(bytes.get()+offset) = htons(temp_nd_size);
		offset += NON_DETERMINISM_SIZE_SIZE;
		
		//write down the nondeterminism
		memcpy( (void*)(bytes.get()+offset), (const void*)temp_nd.get(), temp_nd_size );
		offset += temp_nd_size;
		
		//write down the command batch size
		*(uint16_t*)(bytes.get()+offset) = htons(temp_commands_size);
		offset += CE_COMMAND_BATCH_SIZE_SIZE;
		
		//write down the entry coutn
		*(uint16_t*)(bytes.get()+offset) = htons(temp_entry_count);
		offset += ENTRY_COUNT_SIZE;
		
		//write down the command batch
		memcpy( (void*)(bytes.get()+offset), (const void*)temp_commands.get(), temp_commands_size );
		offset += temp_commands_size;
		
		if(bytes_size != offset)
		{
			fprintf(stderr,"CertificateEntry::getBytes says: something weird is happening there\n");
			exit(183);
		}		
		
	}
	
	_bytes_size = bytes_size;
	return bytes;
} 	

void CertificateEntry::printInfo()
{
	fprintf(stdout,"CertificateEntry information:\n");
	//hd.get()->printInfo();
	commands.get()->printInfo();
	//nd.get()->printInfo();
}	
